package com.stark.commons.spring.core.support.jpa;

import java.util.Locale;

import org.apache.commons.lang3.StringUtils;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategy;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;

/**
 * 自定义表名、列名映射策略。
 * <ul>
 * <li>被 "{" 和 "}" 包围的名称，不做处理；</li>
 * <li>否则由驼峰转成 "_" 连接后，全大写。 </li>
 * </ul>
 * @author Ben
 * @since 1.0.0
 * @version 1.0.0
 */
public class UpperCaseStrategy implements PhysicalNamingStrategy {

	@Override
	public Identifier toPhysicalCatalogName(Identifier name,
			JdbcEnvironment jdbcEnvironment) {
		return apply(name, jdbcEnvironment);
	}

	@Override
	public Identifier toPhysicalSchemaName(Identifier name,
			JdbcEnvironment jdbcEnvironment) {
		return apply(name, jdbcEnvironment);
	}

	@Override
	public Identifier toPhysicalTableName(Identifier name,
			JdbcEnvironment jdbcEnvironment) {
		return formatName(name, jdbcEnvironment);
	}

	@Override
	public Identifier toPhysicalSequenceName(Identifier name,
			JdbcEnvironment jdbcEnvironment) {
		return apply(name, jdbcEnvironment);
	}

	@Override
	public Identifier toPhysicalColumnName(Identifier name,
			JdbcEnvironment jdbcEnvironment) {
		return formatName(name, jdbcEnvironment);
	}

	private Identifier apply(Identifier name, JdbcEnvironment jdbcEnvironment) {
		if (name == null) {
			return null;
		}
		StringBuilder builder = new StringBuilder(name.getText().replace('.', '_'));
		for (int i = 1; i < builder.length() - 1; i++) {
			if (isUnderscoreRequired(builder.charAt(i - 1), builder.charAt(i),
					builder.charAt(i + 1))) {
				builder.insert(i++, '_');
			}
		}
		return getIdentifier(builder.toString(), name.isQuoted(), jdbcEnvironment);
	}

	/**
	 * Get an the identifier for the specified details. By default this method will return
	 * an identifier with the name adapted based on the result of
	 * {@link #isCaseInsensitive(JdbcEnvironment)}
	 * @param name the name of the identifier
	 * @param quoted if the identifier is quoted
	 * @param jdbcEnvironment the JDBC environment
	 * @return an identifier instance
	 */
	protected Identifier getIdentifier(String name, boolean quoted,
			JdbcEnvironment jdbcEnvironment) {
		if (isCaseInsensitive(jdbcEnvironment)) {
			name = name.toLowerCase(Locale.ROOT);
		}
		return new Identifier(name, quoted);
	}

	/**
	 * Specify whether the database is case sensitive.
	 * @param jdbcEnvironment the JDBC environment which can be used to determine case
	 * @return true if the database is case insensitive sensitivity
	 */
	protected boolean isCaseInsensitive(JdbcEnvironment jdbcEnvironment) {
		return true;
	}

	private boolean isUnderscoreRequired(char before, char current, char after) {
		return Character.isLowerCase(before) && Character.isUpperCase(current)
				&& Character.isLowerCase(after);
	}
	
	/**
	 * 格式化表名或列名。
	 * <ul>
	 * <li>被 "{" 和 "}" 包围的名称，不做处理；</li>
	 * <li>否则由驼峰转成 "_" 连接后，全大写。 </li>
	 * </ul>
	 * @param name 表名或列名。
	 * @param jdbcEnvironment jdbc 环境参数。
	 * @return 格式化后的表名或列名。
	 */
	private Identifier formatName(Identifier name, JdbcEnvironment jdbcEnvironment) {
		if (name == null) {
			return null;
		}
		
		if (name.getText().startsWith("{") && name.getText().endsWith("}")) {
			String nameText = StringUtils.substringBetween(name.getText(), "{", "}");
			return new Identifier(nameText, name.isQuoted());
		}
		
		name = apply(name, jdbcEnvironment);
		String nameText = name.getText().toUpperCase();
		return new Identifier(nameText, name.isQuoted());
	}

}
